home *** CD-ROM | disk | FTP | other *** search
- /* Internet FTP Server server machine - see RFC 959
- * Copyright 1991 Phil Karn, KA9Q
- *
- * Mods by KO4KS
- */
- #include "global.h"
- #include "ctype.h"
- #include "commands.h"
- #ifdef UNIX
- #include <time.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #endif
- #include "mbuf.h"
- #include "socket.h"
- #include "ftp.h"
- #include "ftpserv.h"
- #include "dirutil.h"
- #include "files.h"
- #include "session.h"
- #include "smtp.h"
- #ifdef CALLSERVER
- /* CD-ROM code by Fred Peachman KB7YW */
- extern char *CDROM; /* buckbook.c: defines CDROM drive letter e.g. "s:" */
-
- #endif /* #ifdef CALLSERVER */
- #ifdef LZW
- #include "lzw.h"
- #endif
- #include "x.h"
-
-
- #if !defined(_lint)
- static char rcsid[] OPTIONAL = "$Id: ftpserv.c,v 1.31 1997/09/14 14:37:46 root Exp root $";
- #endif
-
- #ifdef UNIX
- int ftpsecuritycheck (char *filename, int perms, int mode);
- extern long ACCESSgid, ACCESSuid;
- extern long CREATEgid, CREATEuid;
- extern short CREATEmask;
- extern int CREATEsecure;
-
- #ifndef R_OK
- #define R_OK 4
- #endif
- #ifndef W_OK
- #define W_OK 2
- #endif
- #endif
-
- static int isanonymous (char *name);
- static void ftpserv (int s, void *unused, void *p);
- static int pport (struct sockaddr_in *sock, char *arg);
- static void ftplogin (struct ftpserv *ftp, char *pass);
- static int sendit (struct ftpserv *ftp, const char *command, char *file);
- static int recvit (struct ftpserv *ftp, const char *command, char *file);
- static void sendmsgfile (int s, int num, char *buf, int size, FILE * fp);
- static void SendPasv (int s, struct sockaddr_in *sock);
- extern char *addroot (const char *root, const char *name);
- extern char *defpath (struct cur_dirs *curdirs, char *path);
-
-
- /* Command table */
- static const char *commands[] =
- {
- "user",
- "acct",
- "pass",
- "type",
- "list",
- "cwd",
- "dele",
- "help",
- "quit",
- "retr",
- "stor",
- "port",
- "nlst",
- "pwd",
- "xpwd", /* For compatibility with 4.2BSD */
- "mkd ",
- "xmkd", /* For compatibility with 4.2BSD */
- "xrmd", /* For compatibility with 4.2BSD */
- "rmd ",
- "stru",
- "mode",
- "syst",
- "xmd5",
- "rsme", /* Added by IW0CNB, for resuming interrupted trasnfers */
- "rput",
- "rnfr",
- "rnto",
- "cdup",
- "appe",
- "noop", /* for OS/2 compatibility */
- "size",
- "pasv", /*PASV mod by G4IDE*/
- #ifdef LZW
- "xlzw",
- #endif
- NULLCHAR
- };
-
-
- #if 0
- static char challenge[] = "399 PASS challenge : %016lx\n";
- static char lowmem[] = "421 System overloaded, try again later\n";
- #endif
-
- #ifdef CATALOG
- #include "catalog.h"
-
- #define CAT ftpserv_catalog
-
- #define banner __STR(0)
- #define banner1 __STR(1)
- #define banner2 __STR(2)
- #define badcmd __STR(3)
- #define binwarn __STR(4)
- #define unsupp __STR(5)
- #define givepass __STR(6)
- #define anonokay __STR(7)
- #define logged __STR(8)
- #define loggeda __STR(9)
- #define typeok __STR(10)
- #define only8 __STR(11)
- #define deleok __STR(12)
- #define mkdok __STR(13)
- #define delefail __STR(14)
- #define pwdmsg __STR(15)
- #define badtype __STR(16)
- #define badport __STR(17)
- #define unimp __STR(18)
- #define bye __STR(19)
- #define nodir __STR(20)
- #define cantopen __STR(21)
- #define sending __STR(22)
- #define cantmake __STR(23)
- #define writerr __STR(24)
- #define portok __STR(25)
- #define rxok __STR(26)
- #define txok __STR(27)
- #define noperm __STR(28)
- #define noconn __STR(29)
- #define badcheck __STR(30)
- #define notlog __STR(31)
- #define userfirst __STR(32)
- #define okay __STR(33)
- #define syst __STR(34)
- #define pendingto __STR(35)
- #define badseq __STR(36)
- #define norename __STR(37)
- #define help __STR(38)
- #define filesize __STR(39)
- #define notaplain __STR(40)
- #define nosuchfile __STR(41)
- #define pasvmodestr __STR(42)
- #else
-
- /* Response messages */
- static char banner[] = "220 %s, KA9Q-NOS FTP version %s\n";
- static char banner1[] = "230- Ready on %s";
- static char banner2[] = "230- Total active FTP sessions at %s: %d out of %d maximum\n";
- static char badcmd[] = "500 Unknown command\n";
- static char binwarn[] = "150- Warning: type is ASCII and %s appears to be binary\n";
- static char unsupp[] = "500 Unsupported command or option\n";
- static char givepass[] = "331 Enter PASS command\n";
- static char anonokay[] = "331 Anonymous access, give email address as password\n";
- static char logged[] = "230 Logged in\n";
- static char loggeda[] = "230 Logged in as anonymous, restrictions apply\n";
- static char typeok[] = "200 Type %s OK\n";
- static char only8[] = "501 Only logical bytesize 8 supported\n";
- static char deleok[] = "250 File deleted\n";
- static char mkdok[] = "200 MKD ok\n";
- static char delefail[] = "550 Delete failed: %s\n";
- static char pwdmsg[] = "257 \"%s\" is current directory\n";
- static char badtype[] = "501 Unknown type \"%s\"\n";
- static char badport[] = "501 Bad port syntax\n";
- static char unimp[] = "502 Command not yet implemented\n";
- static char bye[] = "221 Goodbye!\n";
- static char nodir[] = "553 Can't read directory \"%s\": %s\n";
- static char cantopen[] = "550 Can't read file \"%s\": %s\n";
- static char sending[] = "150 Opening data connection for %s %s %s\n"; /*N1BEE*/
- static char cantmake[] = "553 Can't create \"%s\": %s\n";
- static char writerr[] = "552 Write error: %s\n";
- static char portok[] = "200 Port command okay\n";
- static char rxok[] = "226 File received OK\n";
- static char txok[] = "226 File sent OK\n";
- static char noperm[] = "550 Permission denied\n";
- static char noconn[] = "425 Data connection reset\n";
- static char badcheck[] = "425 Bad checksum\n";
- static char notlog[] = "530 Please log in with USER and PASS\n";
- static char userfirst[] = "503 Login with USER first.\n";
- static char okay[] = "200 Ok\n";
- static char syst[] = "215 %s Type: L%d Version: %s\n";
- static char pendingto[] = "350 Rename awaiting new name.\n";
- static char badseq[] = "503 No prior RNFR received - RNTO ignored\n";
- static char norename[] = "550 Can't rename: %s\n";
- static char help[] = "214-The following commands are recognized.\n";
- static char filesize[] = "213 %lu\n";
- static char notaplain[] = "550 %s: not a plain file\n";
- static char nosuchfile[] = "550 %s: No such file\n";
- static char pasvmodestr[] = "227 Entering Passive Mode. %u,%u,%u,%u,%u,%u\n";
- #endif
-
-
- int Sftp = -1; /* Prototype socket for service */
- int FtpUsers = 0;
- static int FtpMaxUsers = 255;
-
- #ifdef FTPTDISC
- static int32 Ftptdiscinit = 0;
-
-
-
- /* Set ftp redundancy timer */
- int
- doftptdisc (int argc, char *argv[], void *p OPTIONAL)
- {
- return setlong (&Ftptdiscinit, "Ftp redundancy timer (sec)", argc, argv);
- }
-
-
-
- static void
- ftp_redundant (struct ftpserv *ftp)
- {
- /* Clean up */
- (void) shutdown (ftp->control, 2);
- close_s (ftp->control);
- if (ftp->data != -1) {
- (void) shutdown (ftp->data, 2);
- close_s (ftp->data);
- ftp->data = -1;
- }
- return;
- }
- #endif
-
-
-
- /* Set ftp Maximum number of connections */
- int
- doftpmaxclients (int argc, char *argv[], void *p OPTIONAL)
- {
- return setint (&FtpMaxUsers, "Maximum Ftp incoming clients", argc, argv);
- }
-
-
-
- /* Start up FTP service */
- int
- ftpstart (int argc, char *argv[], void *p OPTIONAL)
- {
- return (installserver (argc, argv, &Sftp, "FTP listener", IPPORT_FTP,
- INADDR_ANY, "ftpserv", ftpserv, 2048, NULL));
- }
-
-
-
- static void
- sendmsgfile (int s, int num, char *buf, int size, FILE *fp)
- {
-
- while (fgets (buf, size, fp)) {
- rip (buf);
- usprintf (s, "%d- %s\n", num, buf);
- }
- }
-
-
-
- static int
- isanonymous (char *name)
- {
- FILE *fp, *fpsave = NULLFILE;
- int retval = 1;
- char buf[128], *cp;
-
- if ((fp = fopen (Userfile, READ_TEXT)) != NULLFILE) {
- for ( ; ; ) {
- if (fgets (buf, 128, fp) == NULLCHAR) {
- if (fpsave) {
- (void) fclose (fp);
- fp = fpsave;
- fpsave = NULLFILE;
- continue;
- }
- break;
- }
- if (!strnicmp (buf, "#include", 8)) {
- rip (buf);
- cp = skipwhite (&buf[8]);
- fpsave = fp;
- if ((fp = fopen (cp, READ_TEXT)) == NULLFILE) {
- fp = fpsave;
- fpsave = NULLFILE;
- }
- continue;
- }
- if ((cp = strpbrk (buf, " \t")) == NULLCHAR)
- /* Bogus entry */
- continue;
- *cp++ = '\0';
-
- if (!stricmp (name, buf)) {
- retval = 0;
- break; /* Found user */
- }
- }
- if (fpsave)
- (void) fclose (fpsave);
- (void) fclose (fp);
- }
- return (retval);
- }
-
-
-
- static void
- ftpserv (
- int s, /* Socket with user connection */
- void *unused OPTIONAL,
- void *p OPTIONAL
- ) {
- struct ftpserv ftp;
- char const **cmdp, *cp, *mode = NULLCHAR;
- char buf[512], *arg, *file, *cp2;
- time_t t;
- int cnt, i;
- struct sockaddr_in thesocket;
- struct cur_dirs dirs;
- char *rnfrom = NULLCHAR;
- FILE *fpm;
- struct sockaddr_in lsocket; /*PASV mod*/
- struct sockaddr_in lcsocket;
- #if 0
- struct stat cwdstat;
- char *cp1;
- #endif
-
- (void) sockmode (s, SOCK_ASCII);
- memset ((char *) &ftp, 0, sizeof (ftp)); /* Start with clear slate */
- ftp.data = -1;
-
- (void) sockowner (s, Curproc); /* We own it now */
- ftp.control = s;
- /* Set default data port */
- i = SOCKSIZE;
- if (getpeername (s, (char *) &thesocket, &i) != -1) {
- thesocket.sin_port = IPPORT_FTPD;
- ASSIGN (ftp.port, thesocket);
- }
-
- if (++FtpUsers > FtpMaxUsers) {
- usprintf (s, "\n200- Sorry, too many FTP users at %s this time. Try again later!\n", Hostname);
- FtpUsers--;
- return;
- }
-
- #ifdef FTPTDISC
- /* Set the timeout timer - WG7J */
- set_timer (&ftp.tdisc, Ftptdiscinit * 1000L);
- ftp.tdisc.func = (void (*)(void *)) ftp_redundant;
- ftp.tdisc.arg = &ftp;
- start_timer (&ftp.tdisc);
- #endif
-
- #ifdef STATS_USE
- STATS_adduse (1);
- #endif
- #ifdef XSERVER
- xnotify (X_FTP);
- #endif
- log (s, "open FTP");
- strcpy (buf, ETCdir);
- if ((fpm = fopen (strcat (buf, "/banner.ftp"), "r")) != NULL) {
- sendmsgfile (s, 220, buf, sizeof (buf), fpm);
- (void) fclose (fpm);
- }
- usprintf (s, banner, Hostname, Version);
- (void) init_dirs (&dirs);
- ftp.curdirs = &dirs;
- (void) time (&t);
- cp = ctime (&t);
- #if 0
- if ((cp1 = strchr (cp, '\n')) != NULLCHAR)
- *cp1 = '\0';
- #endif
-
- /* Command interpreting loop */
- loop:
- if ((cnt = recvline (s, (unsigned char *) buf, sizeof (buf))) == -1) {
- /* He closed on us */
- goto finish;
- }
- #ifdef FTPTDISC
- /* Reset the timeout timer - WG7J */
- start_timer (&ftp.tdisc);
- #endif
- if (cnt == 0) {
- /* Can't be a legal FTP command */
- usprintf (ftp.control, badcmd);
- goto loop;
- }
- rip (buf);
- /* Translate first word to lower case */
- for (cp2 = buf; *cp2 != ' ' && *cp2 != '\0'; cp2++)
- *cp2 = (char) tolower (*cp2);
- /* Find command in table; if not present, return syntax error */
- for (cmdp = commands; *cmdp != NULLCHAR; cmdp++)
- if (strnicmp (*cmdp, buf, strlen (*cmdp)) == 0)
- break;
- if (*cmdp == NULLCHAR) {
- usprintf (ftp.control, badcmd);
- goto loop;
- }
- #if 0
- tcmdprintf ("Command received was: '%s'\n", buf);
- #endif
- /* Allow only USER, PASS and QUIT before logging in */
- if (ftp.path == NULLCHAR) {
- switch (cmdp - commands) {
- case USER_CMD:
- case PASS_CMD:
- case QUIT_CMD:
- break;
- default:
- usprintf (ftp.control, notlog);
- goto loop;
- }
- }
- arg = &buf[strlen (*cmdp)];
- while (*arg == ' ')
- arg++;
-
- /* Execute specific command */
- switch (cmdp - commands) {
- case USER_CMD:
- free (ftp.username);
- ftp.username = strdup (arg);
- #if 0
- if(sp_user(ftp.username)) {
- time(&ftp.ttim);
- usprintf(ftp.control,challenge,ftp.ttim);
- } else
- #endif
- if (isanonymous (arg))
- usprintf (ftp.control, anonokay);
- else
- usprintf (ftp.control, givepass);
- break;
- case TYPE_CMD:
- switch (arg[0]) {
- case 'A':
- case 'a': /* Ascii */
- ftp.type = ASCII_TYPE;
- usprintf (ftp.control, typeok, arg);
- break;
- case 'l':
- case 'L':
- while (*arg != ' ' && *arg != '\0')
- arg++;
- if (*arg == '\0' || *++arg != '8') {
- usprintf (ftp.control, only8);
- break;
- }
- ftp.type = LOGICAL_TYPE;
- ftp.logbsize = 8;
- usprintf (ftp.control, typeok, arg);
- break;
- case 'B':
- case 'b': /* Binary */
- case 'I':
- case 'i': /* Image */
- ftp.type = IMAGE_TYPE;
- usprintf (ftp.control, typeok, arg);
- break;
- default: /* Invalid */
- usprintf (ftp.control, badtype, arg);
- break;
- }
- break;
- case QUIT_CMD:
- usprintf (ftp.control, bye);
- goto finish;
- case RNFR_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- if (!permcheck (ftp.path, ftp.perms, RNFR_CMD, file))
- usprintf (ftp.control, noperm);
- else if (access (file, 6))
- usprintf (ftp.control, cantopen, file, SYS_ERRLIST(errno));
- else {
- usprintf (ftp.control, pendingto);
- rnfrom = strdup (file);
- }
- free (file);
- break;
- case RNTO_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- if (rnfrom == NULLCHAR)
- usprintf (ftp.control, badseq);
- else {
- if (!permcheck (ftp.path, ftp.perms, RNTO_CMD, file))
- usprintf (ftp.control, noperm);
- else {
- if (rename (rnfrom, file) == -1)
- usprintf (ftp.control, norename, SYS_ERRLIST(errno));
- else
- usprintf (ftp.control, okay);
- }
- free (rnfrom);
- rnfrom = NULLCHAR;
- }
- free (file);
- break;
- case RETR_CMD:
- case RSME_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- switch (ftp.type) {
- case IMAGE_TYPE:
- case LOGICAL_TYPE:
- mode = READ_BINARY;
- break;
- default:
- case ASCII_TYPE:
- mode = READ_TEXT;
- break;
- }
- if (!permcheck (ftp.path, ftp.perms, RETR_CMD, file))
- usprintf (ftp.control, noperm);
- #ifdef UNIX
- else if (!ftpsecuritycheck (file, ftp.perms, R_OK) || (ftp.fp = fopen (file, mode)) == NULLFILE)
- #else
- else if ((ftp.fp = fopen (file, mode)) == NULLFILE)
- #endif
- usprintf (ftp.control, cantopen, file, SYS_ERRLIST(errno));
- else {
- if ((cmdp - commands) == RSME_CMD) {
- log (ftp.control, "RSME %s", file);
- if (ftp.type == ASCII_TYPE && isbinary (ftp.fp))
- usprintf (ftp.control, binwarn, file);
-
- (void) sendit (&ftp, "RSME", file);
- } else {
- log (ftp.control, "RETR %s", file);
- if (ftp.type == ASCII_TYPE && isbinary (ftp.fp))
- usprintf (ftp.control, binwarn, file);
-
- (void) sendit (&ftp, "RETR", file);
- }
- }
- free (file);
- break;
- case SIZE_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- if (!permcheck (ftp.path, ftp.perms, RETR_CMD, file))
- usputs (ftp.control, noperm);
- else if ((ftp.fp = fopen (file, READ_BINARY)) != NULLFILE) {
- usprintf (ftp.control, filesize, filelength (fileno (ftp.fp)));
- (void) fclose (ftp.fp);
- } else if (!access (file, 0))
- usprintf (ftp.control, notaplain, file);
- else
- usprintf (ftp.control, nosuchfile, file);
-
- free (file);
- break;
- case STOR_CMD:
- cp = "STOR";
- goto store2;
- case APPE_CMD:
- cp = "APPE";
- goto store2;
- case RPUT_CMD:
- cp = "RPUT";
- store2:
- file = addroot (ftp.curdirs->dir, arg);
- switch (ftp.type) {
- case IMAGE_TYPE:
- case LOGICAL_TYPE:
- if (cmdp - commands != STOR_CMD)
- mode = APPEND_BINARY;
- else
- mode = WRITE_BINARY;
- break;
- default:
- case ASCII_TYPE:
- if (cmdp - commands != STOR_CMD)
- mode = APPEND_TEXT;
- else
- mode = WRITE_TEXT;
- break;
- }
- if (!permcheck (ftp.path, ftp.perms, cmdp - commands, file))
- usprintf (ftp.control, noperm);
- #ifdef UNIX
- else if (!ftpsecuritycheck (file, ftp.perms, W_OK) || (ftp.fp = fopen (file, mode)) == NULLFILE)
- #else
- else if ((ftp.fp = fopen (file, mode)) == NULLFILE)
- #endif
- usprintf (ftp.control, cantmake, file, SYS_ERRLIST(errno));
- else {
- log (ftp.control, "%s %s", cp, file);
- (void) recvit (&ftp, cp, file);
- #ifdef UNIX
- if (CREATEsecure) {
- (void) chown (file, (uid_t) CREATEuid, (gid_t) CREATEgid);
- (void) chmod (file, (mode_t) CREATEmask);
- }
- #endif
- }
- free (file);
- break;
- case PORT_CMD:
- if (pport (&ftp.port, arg) == -1)
- usprintf (ftp.control, badport);
- else
- usprintf (ftp.control, portok);
-
- break;
- #ifndef CPM
- case LIST_CMD:
- case NLST_CMD:
- file = addroot (ftp.curdirs->dir, defpath (ftp.curdirs, arg));
- if (!permcheck (ftp.path, ftp.perms, RETR_CMD, file))
- usprintf (ftp.control, noperm);
- else {
- #ifdef UNIX
- if (ftpsecuritycheck (file, ftp.perms, R_OK)) {
-
- #endif
- cp = Command->curdirs->dir;
- Command->curdirs->dir = ftp.curdirs->dir;
- if ((ftp.fp = dir (file, ((cmdp - commands) == LIST_CMD) ? 2 : 0)) == NULLFILE)
- usprintf (ftp.control, nodir, file, SYS_ERRLIST(errno));
- else
- (void) sendit (&ftp, ((cmdp - commands) == LIST_CMD) ? "LIST" : "NLST", file);
- Command->curdirs->dir = cp;
- #ifdef UNIX
- } else
- usprintf (ftp.control, noperm);
- #endif
- }
- free (file);
- break;
- case CDUP_CMD:
- sprintf (arg, ".."); /* and fall through */
- case CWD_CMD:
- #ifdef old_CALLSERVER
- /* if the requested path contains the CROM drive letter: */
- if (CDROM != NULLCHAR && strnicmp (CDROM, arg, 2) == 0) {
- if (strchr (arg, '/') == NULLCHAR) {
- file = (char *) mallocw (strlen (arg) + 2);
- sprintf (file, "%s/", arg);
- } else
- file = strdup (arg);
- if (!permcheck (ftp.path, ftp.perms, RETR_CMD, file)) {
- usprintf (ftp.control, noperm);
- free (file);
- #ifdef MSDOS
- /* Don'tcha just LOVE %%$#@!! MS-DOS? - which is what we are running */
- } else if (file[2] == '/' || access (file, 0) == 0) {
- #else
- } else if (access (file, 0) == 0) { /* See if it exists */
- #endif
- /* Succeeded, record in control block */
- free (ftp.cd);
- ftp.cd = file;
- usprintf (ftp.control, "You may return to your default drive & directory by entering:\n\t\t\"cd %s\"\n\n", ftp.path);
- usprintf (ftp.control, pwdmsg, file);
- } else {
- /* Failed, don't change anything */
- usprintf (ftp.control, nodir, file, SYS_ERRLIST(errno));
- free (file);
- }
- break;
- }
- /* requested path does not contain CDROM drive letter: */
- /* if current dir is in CDROM - and a "off-root" is requested:
- go back to default path. */
- if ((CDROM != NULLCHAR && strnicmp (ftp.cd, CDROM, 2) == 0) && (arg[0] == '/')) {
- free (ftp.cd);
- ftp.cd = strdup (ftp.path); /* go back to default path */
- }
- #endif /* #ifdef CALLSERVER */
- if (*arg == '/' || *arg == '\\')
- file = addroot (ftp.path, &arg[1]);
- else
- file = addroot (ftp.curdirs->dir, arg);
- if (!permcheck (ftp.path, ftp.perms, RETR_CMD, file))
- usprintf (ftp.control, noperm);
- else if (dir_ok (file, ftp.curdirs)) {
- /* Succeeded */
- /* If exists, send the contents of 'desc.ftp' in the new
- * directory...
- */
- strncpy (buf, file, 512);
- if ((fpm = fopen (strcat (buf, "/desc.ftp"), "r")) != NULL) {
- sendmsgfile (ftp.control, 257, buf, sizeof (buf), fpm);
- (void) fclose (fpm);
- }
- #ifndef MSDOS
- /* If exists, send the contents of '.message' in the new
- * directory...
- */
- strncpy (buf, file, 512);
- if ((fpm = fopen (strcat (buf, "/.message"), "r")) != NULL) {
- sendmsgfile (ftp.control, 257, buf, sizeof (buf), fpm);
- (void) fclose (fpm);
- }
- #endif
-
- usprintf (ftp.control, pwdmsg,
- (!strcmp (ftp.root, ftp.curdirs->dir)) ? "/"
- : (!strncmp (ftp.root, ftp.curdirs->dir, strlen (ftp.root)))
- ? &ftp.curdirs->dir[strlen (ftp.root)]
- : ftp.curdirs->dir);
- } else
- /* Failed, nothing changed */
- usprintf (ftp.control, nodir, arg, SYS_ERRLIST(errno));
- free (file);
- break;
- case XPWD_CMD:
- case PWD_CMD:
- usprintf (ftp.control, pwdmsg,
- (!strcmp (ftp.root, ftp.curdirs->dir)) ? "/"
- : (!strncmp (ftp.root, ftp.curdirs->dir, strlen (ftp.root)))
- ? &ftp.curdirs->dir[strlen (ftp.root)]
- : ftp.curdirs->dir);
- break;
- #else
- case LIST_CMD:
- case NLST_CMD:
- case CWD_CMD:
- case XPWD_CMD:
- case PWD_CMD:
- #endif
- case ACCT_CMD:
- usprintf (ftp.control, unimp);
- break;
- case HELP_CMD:
- usputs (ftp.control, help);
- for (cmdp = commands, i = buf[0] = 0; *cmdp != NULLCHAR; cmdp++) {
- strcat (buf, " ");
- strcat (buf, *cmdp);
- if (strlen (*cmdp) == 3)
- strcat (buf, " ");
- if (i++ == 9) {
- (void) strupr (buf);
- usprintf (ftp.control, "%s\n", buf);
- i = buf[0] = 0;
- }
- }
- if (i) {
- (void) strupr (buf);
- usprintf (ftp.control, "%s\n", buf);
- }
- usprintf (ftp.control, "214 Report problems to sysop@%s\n", Hostname);
- break;
- case NOOP_CMD:
- usputs (ftp.control, okay);
- break;
- case DELE_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- if (!permcheck (ftp.path, ftp.perms, DELE_CMD, file))
- usprintf (ftp.control, noperm);
- else if (unlink (file) == 0) {
- log (ftp.control, "DELE %s", file);
- usprintf (ftp.control, deleok);
- } else
- usprintf (ftp.control, delefail, SYS_ERRLIST(errno));
-
- free (file);
- break;
- case PASS_CMD:
- if (ftp.username == NULLCHAR)
- usprintf (ftp.control, userfirst);
- else
- ftplogin (&ftp, arg);
- break;
- #ifndef CPM
- case XMKD_CMD:
- case MKD_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- if (!permcheck (ftp.path, ftp.perms, MKD_CMD, file))
- usprintf (ftp.control, noperm);
- else if (mkdir (file, (mode_t) 0777) == 0) {
- log (ftp.control, "MKD %s", file);
- usprintf (ftp.control, mkdok);
- } else
- usprintf (ftp.control, cantmake, file, SYS_ERRLIST(errno));
-
- free (file);
- break;
- case XRMD_CMD:
- case RMD_CMD:
- file = addroot (ftp.curdirs->dir, arg);
- if (!permcheck (ftp.path, ftp.perms, RMD_CMD, file))
- usprintf (ftp.control, noperm);
- else if (rmdir (file) == 0) {
- log (ftp.control, "RMD %s", file);
- usprintf (ftp.control, deleok);
- } else
- usprintf (ftp.control, delefail, SYS_ERRLIST(errno));
-
- free (file);
- break;
- case STRU_CMD:
- if (tolower (arg[0]) != 'f')
- usprintf (ftp.control, unsupp);
- else
- usprintf (ftp.control, okay);
- break;
- case MODE_CMD:
- if (tolower (arg[0]) != 's')
- usprintf (ftp.control, unsupp);
- else
- usprintf (ftp.control, okay);
- break;
- case SYST_CMD:
- usprintf (ftp.control, syst, System, NBBY, Version);
- break;
- case PASV_CMD: /*PASV mod*/
- /* Send the PASV message. Use the IP address
- * on the local end of our control connection. */
- if (ftp.data != -1) /* left over error - kill the socket first */
- close_s (ftp.data);
-
- ftp.data = socket (AF_INET, SOCK_STREAM, 0);
- (void) listen (ftp.data, 0);
- i = SOCKSIZE;
- (void) getsockname (ftp.data, (char *) &lsocket, &i);
- if (!i)
- break;
- i = SOCKSIZE;
- (void) getsockname (ftp.control, (char *) &lcsocket, &i);
- if (!i)
- break;
- lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
- /* send the address to the client. */
- SendPasv (ftp.control, &lsocket);
- break;
- case XMD5_CMD: /*PASV mod*/
- file = addroot (ftp.curdirs->dir, arg);
- switch (ftp.type) {
- case IMAGE_TYPE:
- case LOGICAL_TYPE:
- mode = READ_BINARY;
- break;
- default:
- case ASCII_TYPE:
- mode = READ_TEXT;
- break;
- }
- if (!permcheck (ftp.path, ftp.perms, RETR_CMD, file))
- usprintf (ftp.control, noperm);
- #ifdef UNIX
- else if (!ftpsecuritycheck (file, ftp.perms, R_OK) || (ftp.fp = fopen (file, mode)) == NULLFILE)
- #else
- else if ((ftp.fp = fopen (file, mode)) == NULLFILE)
- #endif
- usprintf (ftp.control, cantopen, file, SYS_ERRLIST(errno));
- else {
- char hash[16];
-
- log (ftp.control, "XMD5 %s", file);
- if (ftp.type == ASCII_TYPE && isbinary (ftp.fp))
- usprintf (ftp.control, binwarn, file);
-
- (void) md5hash (ftp.fp, hash, ftp.type == ASCII_TYPE);
- (void) fclose (ftp.fp);
- ftp.fp = NULLFILE;
- usprintf (ftp.control, "200 ");
- for (i = 0; i < 16; i++)
- usprintf (ftp.control, "%02x", hash[i] & 0xff);
- usprintf (ftp.control, " %s\n", file);
- }
- free (file);
- break;
- #ifdef LZW
- case XLZW_CMD:
- if (ftp.lzw)
- usprintf (ftp.control, "550 Already using LZW compression\n");
- else {
- usprintf (ftp.control, okay);
- sscanf (&buf[5], "%d %d", &ftp.lzwbits, &ftp.lzwmode);
- ftp.lzw = 1;
- }
- break;
- default:
- break;
- #endif
- }
- #endif
- goto loop;
-
- finish:
-
- #ifdef FTPTDISC
- stop_timer (&ftp.tdisc);
- #endif
-
- log (ftp.control, "close FTP from '%s'", ftp.username);
- FtpUsers--;
- #ifdef XSERVER
- xnotify (X_FTP);
- #endif
- /* Clean up */
- close_s (ftp.control);
- if (ftp.data != -1)
- close_s (ftp.data);
- if (ftp.fp != NULLFILE)
- (void) fclose (ftp.fp);
- free (ftp.username);
- free (ftp.path);
- free_dirs (&dirs);
- free (rnfrom);
- }
-
-
-
- /* Shut down FTP server */
- int
- ftp0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
- {
- return (deleteserver (&Sftp));
- }
-
-
-
- static
- int
- pport (struct sockaddr_in *sock, char *arg)
- {
- uint32 n;
- int i;
-
- n = 0;
- for (i = 0; i < 4; i++) {
- n = (uint32) atoi (arg) + (n << 8);
- if ((arg = strchr (arg, ',')) == NULLCHAR)
- return -1;
- arg++;
- }
- sock->sin_addr.s_addr = n;
- n = (uint32) atoi (arg);
- if ((arg = strchr (arg, ',')) == NULLCHAR)
- return -1;
- arg++;
- n = (uint32) atoi (arg) + (n << 8);
- sock->sin_port = (int16) n;
- return 0;
- }
-
-
-
- /* Attempt to log in the user whose name is in ftp->username and password
- * in pass
- */
- static void
- ftplogin (struct ftpserv *ftp, char *pass)
- {
- char *path, buf[128], *cp;
- char *p, *cp1;
- time_t t;
- FILE *fp;
- int anony = 0;
-
- path = mallocw (200);
- if ((ftp->perms = userlogin (ftp->username, pass, &path, 200, &anony)) == -1) {
- log (ftp->control, "FTP login refused - '%s'", ftp->username);
- usprintf (ftp->control, noperm);
- free (path);
- return;
- }
- /* Set up current directory and path prefix */
- ftp->path = strdup (path);
- cp = strdup (path);
- if ((cp1 = strchr (cp, ';')) != NULLCHAR)
- *cp1 = '\0';
- if ((cp1 = strchr (cp, '=')) != NULLCHAR)
- *cp1 = '\0';
- #ifdef MSDOS
- if (*cp == '/' || *cp == '\\') {
- sprintf (buf, "%c:%s", ftp->curdirs->drv + '`', cp);
- ftp->root = strdup (buf);
- } else
- #endif
- ftp->root = strdup (cp); /*lint !e539 */
- free (cp);
-
- {
- FILE *out;
-
- sprintf (buf, "%s/ftp.log", LOGdir);
- if ((out = fopen (buf, APPEND_TEXT)) != NULLFILE) {
- time_t tt;
-
- (void) time (&tt);
- fprintf (out, "FTP from %s (%s) on %s", ftp->username, pass, ptime (&tt));
- (void) fclose (out);
- }
- }
-
- (void) time (&t);
- cp = ctime (&t);
- usprintf (ftp->control, banner1, cp);
- usprintf (ftp->control, banner2, Hostname, FtpUsers, FtpMaxUsers);
-
- /* everyone gets the ftpmotd file, if it exists */
- if ((fp = fopen (Ftpmotd, "r")) != NULL) {
- sendmsgfile (ftp->control, 230, buf, sizeof (buf), fp);
- (void) fclose (fp);
- }
- /* if there is a message.ftp file in the login dir, send it too */
- sprintf (buf, "%s%s%s", path, (path[strlen (path) - 1] == '/') ? "" : "/", "message.ftp");
- if ((fp = fopen (buf, "r")) != NULL) {
- sendmsgfile (ftp->control, 230, buf, sizeof (buf), fp);
- (void) fclose (fp);
- }
- path = strdup (ftp->path);
- if ((p = strpbrk (path, "=;")) != NULLCHAR)
- *p = 0;
- if (dir_ok (path, ftp->curdirs)) {
- /* Succeeded */
- /* If exists, send the contents of 'desc.ftp' in the new directory... */
- sprintf (buf, "%s/desc.ftp", path);
- if ((fp = fopen (buf, "r")) != NULL) {
- sendmsgfile (ftp->control, 230, buf, sizeof (buf), fp);
- (void) fclose (fp);
- }
- }
- free (path);
- if (!anony) {
- usprintf (ftp->control, logged);
- log (ftp->control, "FTP login - '%s'", ftp->username);
- } else {
- usprintf (ftp->control, loggeda);
- log (ftp->control, "FTP anonymous login - '%s' (%s)", ftp->username, pass);
- }
- usflush (ftp->control);
- kwait (NULL);
- }
-
-
-
- #ifdef MSDOS
- /* Illegal characters in a DOS filename */
- static char badchars[] = "\"[]|<>+=;,";
- #endif
-
-
-
- /* Return 1 if the file operation is allowed, 0 otherwise */
- int
- permcheck (char *path, long perms, int op, char *file)
- {
- char *cp, *cp1;
- int newperms;
- int match = FALSE;
- int notabove = FALSE;
-
- if (file == NULLCHAR || path == NULLCHAR)
- return 0; /* Probably hasn't logged in yet */
-
- /* To get to the CDROM - EVERYBODY gets read privs, regardless of what
- /ftpusers has to say about it!!! - kb7yw */
-
- #ifdef CALLSERVER
- if (CDROM != NULLCHAR && strnicmp (file, CDROM, 2) == 0) {
- /* Check for characters illegal in MS-DOS file names */
- for (cp = badchars; *cp != '\0'; cp++) {
- if (strchr (&file[2], *cp) != NULLCHAR)
- return 0;
- }
-
- switch (op) { /* What to do when the user is on the cd-rom drive */
- case RETR_CMD: /* Everybody gets read privs regardless of ftpusers */
- /* User has permission to read files */
- return 1;
- case DELE_CMD:
- case RMD_CMD:
- /* User must not have permission to (over)write files */
- case STOR_CMD:
- case MKD_CMD:
- /* User must NOT have permission to (over)write files */
- return 0;
- } /* switch(op) */
- } /* if strncmp(.... */
- # endif /* #ifdef CALLSERVER */
- #ifndef MAC
- /* The target file must be under the user's allowed search path */
- /* We let them specify multiple paths using path;path... -russ */
- /* Allow /nos/public;/nos/public/incoming=3 style path statements -bruce */
- for (cp = path; *cp != '\0'; cp = cp1 + 1) {
- char *cp2;
-
- newperms = perms;
- if ((cp1 = strchr (cp, ';')) == NULLCHAR)
- cp1 = &cp[strlen(cp) - 1];
- cp2 = strchr (cp, '=');
- if (!cp2 || (cp2 > cp1))
- cp2 = cp1;
- else
- newperms = atoi (cp2 + 1);
- /* Take care of the case when we have a path statement in ftpusers
- like: /nos/public;/nos/public/incoming=3 and the user cwd's to
- /nos. Make sure the user is not above the smallest length path
- so that we can have path statements like: /nos/public;/nos=1;/nos...
- */
- if ((int) strlen (file) >= (cp2 - cp))
- notabove = TRUE;
- if (!strnicmp (file, cp, (unsigned) (cp2 - cp))
- #ifdef MSDOS
- || !strnicmp (file + 2, cp, cp2 - cp)
- #endif
- ) {
- match = TRUE;
- perms = newperms;
- }
- }
- /* We must have both a match and not be above the smallest level to
- continue */
- if (!match || !notabove)
- return 0;
- #endif
-
- #ifdef MSDOS
- /* Check for characters illegal in MS-DOS file names */
- for (cp = badchars; *cp != '\0'; cp++) {
- if (strchr (file, *cp) != NULLCHAR)
- return 0;
- }
- #endif
-
- switch (op) {
- case RETR_CMD:
- /* User must have permission to read files */
- if (perms & FTP_READ)
- return 1;
- return 0;
- case DELE_CMD:
- case RMD_CMD:
- case RPUT_CMD:
- case APPE_CMD:
- /* User must have permission to (over)write files */
- if (perms & FTP_WRITE)
- return 1;
- return 0;
- case RNFR_CMD:
- case RNTO_CMD:
- case STOR_CMD:
- case MKD_CMD:
- /* User must have permission to (over)write files, or permission
- * to create them if the file doesn't already exist
- */
- if (perms & FTP_WRITE)
- return 1;
- if (access (file, 2) == -1 && (perms & FTP_CREATE))
- return 1;
- return 0;
- default:
- break;
- }
- return 0; /* "can't happen" -- keep lint happy */
- }
-
-
-
- static int
- sendit (struct ftpserv *ftp, const char *command, char *file)
- {
- long total, starting;
- unsigned long check;
- struct sockaddr_in dport;
- char *cp, *cp2;
- char fsizetext[20]; /* N1BEE */
- int pasv = 0;
-
- if (ftp->data != -1)
- pasv = 1;
-
- fsizetext[0] = 0;
- if (!pasv) {
- ftp->data = socket (AF_INET, SOCK_STREAM, 0);
- dport.sin_family = AF_INET;
- dport.sin_addr.s_addr = INADDR_ANY;
- dport.sin_port = IPPORT_FTPD;
- (void) bind (ftp->data, (char *) &dport, SOCKSIZE);
- }
- sprintf (fsizetext, "(%lu bytes)", filelength (fileno (ftp->fp)));
- usprintf (ftp->control, sending, command, (strlen (file) == strlen (ftp->path)) ? "/" : &file[strlen (ftp->path)], fsizetext); /* N1BEE */
- if (!pasv) {
- if (connect (ftp->data, (char *) &ftp->port, SOCKSIZE) == -1) {
- (void) fclose (ftp->fp);
- ftp->fp = NULLFILE;
- close_s (ftp->data);
- ftp->data = -1;
- usprintf (ftp->control, noconn);
- return -1;
- }
- } else { /* PASV mode */
- /* wait for the client to open the connection */
- /* ftp->data has been setup already */
- (void) accept (ftp->data, NULLCHAR, (int *) NULL);
- }
- if (strcmp (command, "RSME") == 0) {
- total = -1;
- cp = mallocw (40);
- if (recvline (ftp->control, (unsigned char *) cp, 40) == -1) {
- free (cp);
- goto send_err;
- }
- starting = atol (cp);
- /* If checksum field is not present go on anyway, for compatibility
- * with previous scheme. If present check it and barf if wrong.
- */
- cp2 = strchr (cp, ' ');
- if (cp2 != NULLCHAR) {
- check = (unsigned long) atol (cp2);
- check -= checksum (ftp->fp, starting);
- if (check != 0) {
- free (cp);
- usprintf (ftp->control, badcheck);
- (void) shutdown (ftp->data, 1); /* Blow away data connection */
- goto send_err;
- }
- } else if (fseek (ftp->fp, starting, SEEK_SET) != 0) {
- free (cp);
- usprintf (ftp->control, noconn);
- (void) shutdown (ftp->data, 2); /* Blow away data connection */
- goto send_err;
- }
- }
- #ifdef FTPTDISC
- /* Turn off the timeout timer here, some ftp's could
- * take a long time with sloooow packet channels - WG7J
- */
- stop_timer (&ftp->tdisc);
- #endif
-
- #ifdef LZW
- if (ftp->lzw)
- lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);
- #endif
-
- /* Do the actual transfer */
- total = sendfile (ftp->fp, ftp->data, ftp->type, 0);
-
- #ifdef FTPTDISC
- /* And turn it back on now */
- start_timer (&ftp->tdisc);
- #endif
-
- if (total == -1) {
- /* An error occurred on the data connection */
- usprintf (ftp->control, noconn);
- (void) shutdown (ftp->data, 2); /* Blow away data connection */
- } else
- usprintf (ftp->control, txok);
-
- send_err:
- (void) fclose (ftp->fp);
- ftp->fp = NULLFILE;
- close_s (ftp->data);
- ftp->data = -1;
- #ifdef LZW
- ftp->lzw = 0;
- #endif
- if (total == -1)
- return -1;
- else
- return 0;
- }
-
-
-
- static int
- recvit (struct ftpserv *ftp, const char *command, char *file)
- {
- struct sockaddr_in dport;
- long total, starting;
- int pasv = 0;
-
- if (ftp->data != -1)
- pasv = 1;
-
- if (!pasv) {
- ftp->data = socket (AF_INET, SOCK_STREAM, 0);
- dport.sin_family = AF_INET;
- dport.sin_addr.s_addr = INADDR_ANY;
- dport.sin_port = IPPORT_FTPD;
- (void) bind (ftp->data, (char *) &dport, SOCKSIZE);
- }
- usprintf (ftp->control, sending, command, file, "");
-
- if (!pasv) {
- if (connect (ftp->data, (char *) &ftp->port, SOCKSIZE) == -1) {
- (void) fclose (ftp->fp);
- ftp->fp = NULLFILE;
- close_s (ftp->data);
- ftp->data = -1;
- usprintf (ftp->control, noconn);
- return -1;
- }
- } else { /* PASV mode */
- /* wait for the client to open the connection */
- /* ftp->data has been setup already */
- (void) accept (ftp->data, NULLCHAR, (int *) NULL);
- }
- if (strcmp (command, "APPE") == 0)
- fseek (ftp->fp, 0, SEEK_END);
- if (strcmp (command, "RPUT") == 0) {
- if ((starting = (long) getsize (ftp->fp)) == -1)
- starting = 0L;
- usprintf (ftp->control, "%lu %lu\n", starting, checksum (ftp->fp, starting));
- fseek (ftp->fp, starting, SEEK_SET);
- }
- #ifdef FTPTDISC
- /* Turn of the timeout timer here; some ftp's could
- * take a long time with sloooow packet channels - WG7J
- */
- stop_timer (&ftp->tdisc);
- #endif
-
- #ifdef LZW
- if (ftp->lzw)
- lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);
- #endif
-
- /* Do the actual transfer */
- total = recvfile (ftp->fp, ftp->data, ftp->type, 0);
-
- #ifdef FTPTDISC
- /* And turn it back on now */
- start_timer (&ftp->tdisc);
- #endif
-
- #ifdef CPM
- if (ftp->type == ASCII_TYPE)
- putc (CTLZ, ftp->fp);
- #endif
- if (total == -1) {
- /* An error occurred while writing the file */
- usprintf (ftp->control, writerr, SYS_ERRLIST(errno));
- (void) shutdown (ftp->data, 2); /* Blow it away */
- } else
- usprintf (ftp->control, rxok);
- close_s (ftp->data);
- ftp->data = -1;
- (void) fclose (ftp->fp);
- ftp->fp = NULLFILE;
- #ifdef LZW
- ftp->lzw = 0;
- #endif
- if (total == -1)
- return -1;
- else
- return 0;
- }
-
-
-
- #ifdef UNIX
- #if 0
- extern uid_t geteuid (void);
- extern gid_t getegid (void);
- #endif
-
- int
- ftpsecuritycheck (char *filename, int perms, int mode)
- {
- int accval; /*lint -esym(550, accval) */
- int retval;
- int fd;
- char *buf, *cp;
- uid_t ruid = getuid ();
- gid_t rgid = getgid ();
- #if 0
- uid_t euid = geteuid ();
- gid_t egid = getegid ();
- #endif
-
- if (perms & SYSOP_CMD)
- return (1);
- #if 0
- log (-1, "ftpsecuritycheck: file - %s", filename);
- log (-1, "ftpsecuritycheck: mode - %d", mode);
- log (-1, "ftpsecuritycheck: ftpgid - %d", ACCESSgid);
- log (-1, "ftpsecuritycheck: ftpuid - %d", ACCESSuid);
- log (-1, "ftpsecuritycheck: ruid - %d", ruid);
- log (-1, "ftpsecuritycheck: rgid - %d", rgid);
- log (-1, "ftpsecuritycheck: euid - %d", euid);
- log (-1, "ftpsecuritycheck: egid - %d", egid);
- #endif
- (void) setregid ((int16) ACCESSgid, (unsigned short) -1);
- (void) setreuid ((int16) ACCESSuid, (unsigned short) -1);
- #if 0
- log (-1, "ftpsecuritycheck: uid - %d", getuid ());
- log (-1, "ftpsecuritycheck: gid - %d", getgid ());
- log (-1, "ftpsecuritycheck: euid - %d", geteuid ());
- log (-1, "ftpsecuritycheck: egid - %d", getegid ());
- #endif
- retval = ((accval = access (filename, mode)) == 0);
-
- /* for write permissions, you can't JUST use access, if the
- file is a new file being created. */
- if (mode == W_OK && errno == 2) {
- /* if the file doesn't already exist, it takes two steps */
- if (access (filename, 0)) {
- /* first we see if the file CAN be created */
- fd = open (filename, O_CREAT | O_WRONLY | O_TRUNC);
- if (fd != -1) {
- close (fd);
- unlink (filename);
- retval = 1;
- }
- }
- }
- /* now we PROBABLY know whether we can do this, but we must also
- check the directory, to see if we have write permissions, if it
- is a write
- */
- if (mode == W_OK) {
- buf = strdup (filename);
- cp = strrchr (buf, '/');
- if (cp)
- *cp = 0;
- else
- strcpy (buf, ".");
- /* set retval to whether we have write permissions to the directory */
- if (access (buf, mode))
- retval = 0;
- free (buf);
- }
- (void) setreuid (ruid, (unsigned short) -1);
- (void) setregid (rgid, (unsigned short) -1);
- #if 0
- log (-1, "ftpsecuritycheck: neweuid - %d", geteuid ());
- log (-1, "ftpsecuritycheck: newegid - %d", getegid ());
- log (-1, "ftpsecuritycheck: newuid - %d", getuid ());
- log (-1, "ftpsecuritycheck: newgid - %d", getgid ());
- log (-1, "ftpsecuritycheck: accval = %d", accval);
- log (-1, "ftpsecuritycheck: retval = %d", retval);
- log (-1, "ftpsecuritycheck: errno = %d", errno);
- #endif
- if (!retval)
- errno = EACCES;
- return (retval);
- }
- #endif
-
-
-
- /* PASV mod */
- static void
- SendPasv (int s, struct sockaddr_in *sock)
- {
- /* Send PORT a,a,a,a,p,p message */
- usprintf (s, pasvmodestr,
- hibyte (hiword (sock->sin_addr.s_addr)),
- lobyte (hiword (sock->sin_addr.s_addr)),
- hibyte (loword (sock->sin_addr.s_addr)),
- lobyte (loword (sock->sin_addr.s_addr)),
- hibyte (sock->sin_port),
- lobyte (sock->sin_port));
- }
-